#include <stdio.h>
#include <stdlib.h>
#include "uthread.h"
#include <ucontext.h>

int idAtual=0;

void inserir_fila ( thread *novo, Fila *umaFila )
{
	if(novo != NULL)
	{
		if(umaFila->ini == NULL)
		{
			umaFila->ini = novo;
			umaFila->fim = novo;
		}
		else
		{
			umaFila->fim->prox = novo;
			umaFila->fim = novo;
		}
	}
}

thread* pegarPrimeiroFila(Fila *umaFila)
{
	thread *prim;

	if (umaFila == NULL)
		return NULL;
	else{
		prim = umaFila->ini;
		umaFila->ini = umaFila->ini->prox;
		return prim;
	}
}

thread* acharthread(int id, thread* umaFila)
{
	thread *ptAtual=NULL, *ptAnt = NULL;
	thread *contexto=NULL;
	if(umaFila == NULL)
		return NULL;
	else
	{
		ptAtual = umaFila;
		while(ptAtual != NULL && ptAtual->tid != id)
		{
			ptAnt = ptAtual;
			ptAtual = ptAtual->prox;
		}
		if(ptAtual != NULL)
		{
			contexto = ptAtual;
			ptAnt->prox = ptAtual->prox;
		}
		return contexto;
	}
}

int existeNaFila(int id, Fila fila)
{
	thread *ptAtual=NULL;

	if(fila.ini == NULL)
		return 0;
	else
	{
		ptAtual = fila.ini;
		while(ptAtual->tid != id && ptAtual != NULL)
		{
			ptAtual = ptAtual->prox;
		}
		if(ptAtual == NULL)
			return 0;
		else
			return 1;

	}
}




void escalonador( void )
{
	thread *ptr;
	int iden;

	if ((ready.ini != NULL) && (blocked.ini != NULL))
	{

		iden = running->tid;
		inserir_fila(acharthread(iden, blocked.ini), &ready);
		ptr = running;
		free(ptr);
		running = pegarPrimeiroFila(&ready);
		if (running != NULL){
			swapcontext(&schedulerContext, 	&(running->contexto));
		}
	}
}

int libuthread_init()
{
	char* func_stack = (char*) calloc(8192,sizeof(char));
	//stack de 8Kb

	thread *novo;

	//Pegando contexto da main e colocando em running
	if(getcontext(&mainThread) == -1)
		return -1;

	novo = (thread*) malloc(sizeof(thread));
	novo->tid = 0;
	novo->prox = NULL;
	novo->contexto = mainThread;

	running = novo;

	if(getcontext(&schedulerContext) == -1)
		return -1;

	//Criando o contexto do escalonador
	schedulerContext.uc_stack.ss_sp = func_stack;
        schedulerContext.uc_stack.ss_size = 8192;
	schedulerContext.uc_link = &mainThread;

	makecontext(&schedulerContext, escalonador, 0);

	return 0;

}

int uthread_create( void (*start_routine)(), void *arg )
{
	thread *novo;
	//int idAtual=-1, i;

	char* func_stack = (char*) calloc(8192,sizeof(char));
	//stack de 8Kb

	/*i = 1;
	while(Identificadores[i] != 0)
	{
		i++;
	}
	idAtual = i;
	Identificadores[i] = 1;
*/
    idAtual++;
	//Criando o espaço para o contexto
	novo = (thread*) malloc(sizeof(thread));
	novo->tid = idAtual;
	novo->idBloqueante = -1;
	novo->prox = NULL;
	getcontext(&(novo->contexto));
	novo->contexto.uc_stack.ss_sp = func_stack;
	novo->contexto.uc_stack.ss_size = 8192;
	novo->contexto.uc_link = &schedulerContext;

	if(novo->contexto.uc_stack.ss_sp == 0)
		return -1;

	makecontext(&(novo->contexto), start_routine, 1, arg);

	inserir_fila( novo, &ready );

	//Função terminou corretamente
	return novo->tid;

}

void uthread_yield( void )
{
	inserir_fila(running, &ready);
	running = pegarPrimeiroFila(&ready);
	swapcontext(&(ready.fim->contexto), &running->contexto);
}



/*bloqueia a thread em execução até que a thread identificada pelo argumento (
thr) termine. Quando a thread identificada por thrterminar, a funçãouthread_join
retorna um valor inteiro com a seguinte interpretação: se o valor retornado for zero, a função foi corretamente executada, caso contrário um valor diferente de zero é
retornado.*/
int uthread_join(int thr)
{
	// Se existe na fila de ready uma thread com o id thr,entao
	//pode bloquear a thread
    if(existeNaFila(thr, ready))
	{
		thread *runningAnterior = running;
		running->idBloqueante = thr;
		inserir_fila(running, &blocked);
		running = pegarPrimeiroFila(&ready);
		swapcontext(&runningAnterior->contexto, &running->contexto);
		return 0;
	}
	else
		return -1;
}

int uthread_lock(umutex_t lock[])
{

// tudoo errado
    /*if(lock[0].busy==0)
    {
       lock[0].busy=1;
    }
    //poe na fila
    else
    {
        //thread *runningAnterior = running;
        inserir_fila(running, &blocked);
		running = pegarPrimeiroFila(&ready);
		//swapcontext(&runningAnterior->contexto, &running->contexto);
        printf("entro\n");
    }
    */
    return 1;

}
int uthread_unlock (umutex_t lock[])
{
    lock[0].busy=0;
    return 1;

}

